//
// Created by LONG on 2020/10/27.
//


#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "../../include/utils/wheelTimer.h"

#define TICKS_PER_WHEEL 256

struct WtWheel
{
    uint32_t maxDelay;
    uint32_t clock;
    uint32_t wheelNum;
    S_List *wheels;
};

S_WtWheel *WT_CreateWheel(const char *name, uint32_t maxDelay)
{
    uint32_t loop;
    uint32_t lloop;
    uint32_t size;
    uint32_t wheelNum;
    S_WtWheel *wheel;

    wheelNum = (maxDelay + TICKS_PER_WHEEL - 1) / TICKS_PER_WHEEL;
    size = sizeof(S_WtWheel) + (sizeof(S_List) * wheelNum * TICKS_PER_WHEEL);
    wheel = OS_MemCalloc(1, size);
    if (wheel == NULL)
    {
        return NULL;
    }

    wheel->maxDelay = maxDelay;
    wheel->clock = 0;
    wheel->wheelNum = wheelNum;
    wheel->wheels = (S_List *)((char *)wheel + sizeof(S_WtWheel));

    for (loop = 0; loop < wheelNum; loop++)
    {
        for (lloop = 0; lloop < TICKS_PER_WHEEL; lloop++)
        {
            LIST_Init(&wheel->wheels[loop * TICKS_PER_WHEEL + lloop]);
        }
    }

    return wheel;
}

void WT_DestroyWheel(S_WtWheel *wheel)
{
    uint32_t loop;
    uint32_t lloop;

    for (loop = 0; loop < wheel->wheelNum; loop++)
    {
        for (lloop = 0; lloop < TICKS_PER_WHEEL; lloop++)
        {
            LIST_Final(&wheel->wheels[loop * TICKS_PER_WHEEL + lloop]);
        }
    }

    OS_MemFree(wheel);
}

int32_t WT_InitTimer(S_WtTimer *timer)
{
    memset(timer, 0, sizeof(S_WtTimer));
    timer->initFlag = CSP_MODULE_INITIALIZED;
    timer->runState = TIMER_STATE_STOP;

    return CSP_RC_OK;
}

int32_t WT_FinalTimer(S_WtTimer *timer)
{
    if (CSP_MODULE_INITIALIZED == timer->initFlag)
    {
        timer->initFlag = CSP_MODULE_UNINITIALIZED;
        timer->runState = TIMER_STATE_STOP;
        LIST_Remove(&timer->node);
        return CSP_RC_OK;
    }

    return CSP_RC_ER;
}

int32_t WT_StartTimer(S_WtWheel *wheel, S_WtTimer *timer, WheelTimerCallback callback, void *arg, uint32_t type, uint32_t delay)
{
    uint32_t idx;
    uint32_t offset;
    uint32_t clock;

    timer->type = type;
    timer->delay = delay;
    timer->callback = callback;
    timer->arg = arg;

    if (CSP_MODULE_INITIALIZED != timer->initFlag
        || timer->delay > wheel->maxDelay
        || 0 == timer->delay)
    {
        return CSP_RC_ER;
    }

    if (timer->type != TIMER_TYPE_ONCE
        && timer->type != TIMER_TYPE_LOOP)
    {
        return CSP_RC_ER;
    }

    LIST_Remove(&timer->node);

    clock = wheel->clock + timer->delay;
    idx = (clock / TICKS_PER_WHEEL) % wheel->wheelNum;
    offset = clock % TICKS_PER_WHEEL;

    LIST_Append(&wheel->wheels[idx * TICKS_PER_WHEEL + offset], &timer->node);
    timer->runState = TIMER_STATE_START;

    return CSP_RC_OK;
}

int32_t WT_StopTimer(S_WtTimer *timer)
{
    if (CSP_MODULE_INITIALIZED == timer->initFlag)
    {
        timer->runState = TIMER_STATE_STOP;
        LIST_Remove(&timer->node);
        return CSP_RC_OK;
    }

    return CSP_RC_ER;
}

int32_t WT_GetTimerState(S_WtTimer *timer)
{
    return timer->runState;
}

void WT_Run(S_WtWheel *wheel, uint32_t step)
{
    uint32_t loop;
    uint32_t idx;
    uint32_t offset;
    S_List *list;
    S_WtTimer *timer;

    for (loop = 0; loop < step; loop++)
    {
        wheel->clock++;
        idx = (wheel->clock / TICKS_PER_WHEEL) % wheel->wheelNum;
        offset = wheel->clock % TICKS_PER_WHEEL;
        list = &wheel->wheels[idx * TICKS_PER_WHEEL + offset];
        while (!LIST_Empty(list))
        {
            timer = LIST_FIRST_ENTRY(list, S_WtTimer, node);
            LIST_Remove(&timer->node);
            timer->runState = TIMER_STATE_TIMEOUT;
            if (timer->callback)
            {
                timer->callback(timer->arg);
            }
            if (TIMER_TYPE_LOOP == timer->type
                && TIMER_STATE_TIMEOUT == timer->runState)
            {
                WT_StartTimer(wheel, timer, timer->callback, timer->arg, timer->type, timer->delay);
            }
        }
    }
}